/****************************************************************
 * <strong>MFActiveRecordBase泛型基类</strong>
 *
 * <p>
 * 泛型基类
 * </p>
 *
 * @author AndyZhang 2011 cnet2001@163.com
 * @version 0.1
 * ToDo:
 ***************************************************************/
package com.thinkwide.data.ormapping;

import com.thinkwide.data.core.MFString;
import com.thinkwide.data.ormapping.dialect.MFOperateCrossSql;
import com.thinkwide.data.ormapping.page.MFPage;
import com.thinkwide.data.ormapping.sql.MFSqlBase;
import com.thinkwide.data.ormapping.sql.MFSqlDelete;
import com.thinkwide.data.ormapping.sql.MFSqlInsert;
import com.thinkwide.data.ormapping.sql.MFSqlSelect;
import com.thinkwide.data.ormapping.sql.MFSqlUpdate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * ActiveRecord
 *
 * @author AndyZhang
 */
public class MFActiveRecordBase<T> implements Serializable {

    private static Logger logger = LoggerFactory.getLogger(MFActiveRecordBase.class);

    private Class<T> entityClass;

    private MFDataAdapter dataAdapter;

    private static MFDataAdapter getStaticDataAdapter() {
        return MFORMappingGlobe.getDataAdapter();
    }

    private static MFDataAdapter getAutoDataAdapter() {

        return new MFDataAdapter();
    }

    private static <T> void setListDataAdapter(List<T> list, MFDataAdapter adapter) throws Throwable {
        for (T item : list) {
            ((MFActiveRecordBase<?>) item).setDataAdapter(adapter);
            //MFReflect.SetProperty(item, "dataAdapter", adapter);
        }
    }

    private void checkDataAdapter() {

        if (dataAdapter == null) {
            dataAdapter = MFORMappingGlobe.getDataAdapter();
        }
    }

    public MFActiveRecordBase() {
        entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

    }

    public static <T> T findOne(Class<T> classType, String where, Object... args) {
        return findOne(null, classType, where, args);

    }

    public static <T> T findOne(MFDataAdapter dataAdapter, Class<T> classType, String where, Object... args) {

        List<T> list = findList(dataAdapter, classType, where, "", args);
        if (list.size() > 0) {
            return list.get(0);
        } else {
            return null;
        }
    }

    public static <T> T findOneBySql(Class<T> classType, String sql, Object... cmdParams) {
        return findOneBySql(null, classType, sql, cmdParams);

    }

    public static <T> T findOneBySql(MFDataAdapter dataAdapter, Class<T> classType, String sql, Object... cmdParams) {

        List<T> list = findListBySql(dataAdapter, classType, sql, cmdParams);
        if (list.size() > 0) {
            return list.get(0);
        } else {
            return null;
        }
    }

    public static <T> List<T> findList(Class<T> classType) {
        return findList(null, classType, "", "", "");
    }

    public static <T> List<T> findList(MFDataAdapter dataAdapter, Class<T> classType) {
        return findList(dataAdapter, classType, "", "", "");
    }

    public static <T> List<T> findList(Class<T> classType, String where, String orderBy, Object... args) {
        return findList(null, classType, where, orderBy, args);
    }

    public String getTableViewName() {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(entityClass);
        return cInfo.getTableViewName();
    }

    public static <T> String getTableViewName(Class<T> classType) {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(classType);
        return cInfo.getTableViewName();
    }

    /*
     * 参数化获取数据防止SQL注入攻击，参数使用"?"
     */
    public static <T> List<T> findListBySql(Class<T> classType, String sql, Object... cmdParams) {
        return findListBySql(classType, sql, cmdParams);
    }

    /*
     * 参数化获取数据防止SQL注入攻击，参数使用"?"
     */
    public static <T> List<T> findListBySql(MFDataAdapter dataAdapter, Class<T> classType, String sql, Object... cmdParams) {
        ResultSet rs;
        List<T> list = null;
        try {

            if (dataAdapter != null) {
                rs = dataAdapter.getResultSet(sql, cmdParams);
                list = MFDataTransfer.getList(classType, rs);
                setListDataAdapter(list, dataAdapter);
                dataAdapter.closeAuto(rs);
            } else {
                MFDataAdapter da = getAutoDataAdapter();
                rs = da.getResultSet(sql, cmdParams);
                list = MFDataTransfer.getList(classType, rs);
                da.closeAuto(rs);
                da.free();
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

        return list;
    }

    public static <T> List<T> findListBySql(MFDataAdapter dataAdapter, Class<T> classType, String sql) {
        ResultSet rs;
        List<T> list = null;
        try {

            if (dataAdapter != null) {
                rs = dataAdapter.getResultSet(sql);
                list = MFDataTransfer.getList(classType, rs);
                setListDataAdapter(list, dataAdapter);
                dataAdapter.closeAuto(rs);
            } else {
                MFDataAdapter da = getAutoDataAdapter();
                rs = da.getResultSet(sql);
                list = MFDataTransfer.getList(classType, rs);
                da.closeAuto(rs);
                da.free();
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

        return list;
    }

    public static <T> MFPage<T> findPageBySql(MFDataAdapter dataAdapter, Class<T> classType, int beginIndex,
                                              int endIndex, String sql, Object... cmdParams) {
        ResultSet rs;
        List<T> list = null;
        int count = 0;
        try {

            if (dataAdapter != null) {
                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(dataAdapter);
                sql = CrossSQL.getSelectPageSql(sql, beginIndex, endIndex);

                rs = dataAdapter.getResultSet(sql, cmdParams);
                list = MFDataTransfer.getList(classType, rs);
                setListDataAdapter(list, dataAdapter);
                count = dataAdapter.getCount(sql, cmdParams);
                dataAdapter.closeAuto(rs);
            } else {

                MFDataAdapter da = getAutoDataAdapter();

                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(da);
                sql = CrossSQL.getSelectPageSql(sql, beginIndex, endIndex);

                rs = da.getResultSet(sql);
                list = MFDataTransfer.getList(classType, rs);
                count = da.getCount(sql, cmdParams);
                da.closeAuto(rs);
                da.free();
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

        return MFPage.buildByIndex(list, beginIndex, endIndex, count);

    }

    public static <T> MFPage<T> findPage(MFDataAdapter dataAdapter, Class<T> classType, int beginIndex,
                                         int endIndex, String where, String orderBy, Object... args) {
        String sSQL;

        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(classType);

        String rWhere = MFSqlSelect.getWhereConditionSql(where, cInfo.getWhere());
        String rOrderBy = MFSqlSelect.getOrderByConditionSql(orderBy, cInfo.getOrderBy());

        String sTableName = cInfo.getTableViewName();

        rWhere = MFSqlSelect.getStaticSql(rWhere, args);

        ResultSet rs;
        List<T> list = null;
        int count = 0;
        try {

            if (dataAdapter != null) {
                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(dataAdapter);
                sSQL = CrossSQL.getSelectPageSql(sTableName, rWhere, rOrderBy, beginIndex, endIndex);

                rs = dataAdapter.getResultSet(sSQL);
                list = MFDataTransfer.getList(classType, rs);
                setListDataAdapter(list, dataAdapter);
                count = dataAdapter.getCount(sSQL);
                dataAdapter.closeAuto(rs);
            } else {
                MFDataAdapter da = getAutoDataAdapter();

                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(da);
                sSQL = CrossSQL.getSelectPageSql(sTableName, rWhere, rOrderBy, beginIndex, endIndex);

                rs = da.getResultSet(sSQL);
                list = MFDataTransfer.getList(classType, rs);
                count = da.getCount(sSQL);
                da.closeAuto(rs);
                da.free();
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

        return MFPage.buildByIndex(list, beginIndex, endIndex, count);
    }

    public static <T> List<T> findList(MFDataAdapter dataAdapter, Class<T> classType, String where, String orderBy, Object... args) {

        String sSQL;

        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(classType);

        String rWhere = MFSqlSelect.getWhereConditionSql(where, cInfo.getWhere());
        String rOrderBy = MFSqlSelect.getOrderByConditionSql(orderBy, cInfo.getOrderBy());

        String sTableName = cInfo.getTableViewName();

        if (!MFString.isNullOrEmpty(cInfo.getSQL())) {
            sSQL = cInfo.getSQL();

        } else {
            sSQL = String.format(MFSqlSelect.SELECT_SIMPLE, sTableName);

        }

        sSQL = sSQL + MFSqlBase.SPACE + MFSqlSelect.getSelectSql(rWhere, args) + MFSqlBase.SPACE + rOrderBy;


        ResultSet rs;
        List<T> list = null;
        try {

            if (dataAdapter != null) {
                rs = dataAdapter.getResultSet(sSQL);
                list = MFDataTransfer.getList(classType, rs);
                setListDataAdapter(list, dataAdapter);
                dataAdapter.closeAuto(rs);
            } else {
                MFDataAdapter da = getAutoDataAdapter();
                rs = da.getResultSet(sSQL);
                list = MFDataTransfer.getList(classType, rs);
                da.closeAuto(rs);
                da.free();
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

        return list;

    }


    /**
     * @param <T>
     * @param classType
     * @param orderBy
     * @return
     */
    public static <T> List<T> findListOrderBy(Class<T> classType, String orderBy) {
        return findList(classType, "", orderBy, "");

    }

    public static <T> List<T> findListOrderBy(MFDataAdapter dataAdapter, Class<T> classType, String orderBy) {
        return findList(dataAdapter, classType, "", orderBy, "");

    }

    public int insert(MFDataAdapter adapter) throws Throwable {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(entityClass);
        String tableName = cInfo.getTableName();
        Map<String, String> ls = MFDataTransfer.getFieldPropList(this);
        String strFields = MFString.Empty;
        String strValues = MFString.Empty;
        List<Object> argls = new ArrayList<Object>();
        for (Map.Entry<String, String> item : ls.entrySet()) {
            if (MFString.notNullOrEmpty(strFields)) {
                strFields = strFields + MFSqlBase.COMMA + item.getKey();
                strValues = strValues + MFSqlBase.COMMA + MFSqlBase.PARAM;
            } else {
                strFields = item.getKey();
                strValues = MFSqlBase.PARAM;
            }
            Object value = MFReflect.getProperty(this, item.getValue());
            argls.add(value);
        }

        String sSQL = MFSqlInsert.getInsertSql(tableName, strFields, strValues);
        return adapter.execSql(sSQL, argls.toArray());
    }

    public int insert() throws Throwable {
        if (dataAdapter == null) {
            MFDataAdapter da = getAutoDataAdapter();
            int i = insert(da);
            da.free();
            da = null;
            return i;
        } else {
            return insert(dataAdapter);
        }
    }


    public int update(MFDataAdapter adapter) throws Throwable {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(entityClass);
        String tableName = cInfo.getTableName();
        Map<String, String> ls = MFDataTransfer.getFieldPropList(this);
        String strFields = MFString.Empty;

        List<Object> argls = new ArrayList<Object>();
        for (Map.Entry<String, String> item : ls.entrySet()) {
            if (MFString.notNullOrEmpty(strFields)) {
                strFields = strFields + MFSqlBase.COMMA + item.getKey() + MFSqlBase.EQUAL + MFSqlBase.PARAM;

            } else {
                strFields = item.getKey() + MFSqlBase.EQUAL + MFSqlBase.PARAM;
            }
            Object value = MFReflect.getProperty(this, item.getValue());
            argls.add(value);
        }

        String where = MFString.Empty;
        Map<String, String> pls = MFDataTransfer.getPrimaryKeyFieldList(entityClass);

        for (Map.Entry<String, String> item : pls.entrySet()) {

            if (MFString.notNullOrEmpty(where)) {
                where = where + MFSqlBase.AND;
            }
            where = where + item.getKey() + "= ? ";
            Object value = MFReflect.getProperty(this, item.getValue());
            argls.add(value);
        }

        String sSQL = MFSqlUpdate.getUpdateSql(tableName, strFields, where);

        return adapter.execSql(sSQL, argls.toArray());
    }

    public int update() throws Throwable {

        if (dataAdapter == null) {
            MFDataAdapter da = getAutoDataAdapter();
            int i = update(da);
            da.free();
            da = null;
            return i;
        } else {
            return update(dataAdapter);
        }
    }

    public int delete(MFDataAdapter adapter) throws Throwable {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(entityClass);
        String tableName = cInfo.getTableName();
        String where = MFString.Empty;
        List<Object> argls = new ArrayList<Object>();
        Map<String, String> ls = MFDataTransfer.getPrimaryKeyFieldList(entityClass);
        for (Map.Entry<String, String> item : ls.entrySet()) {

            if (MFString.notNullOrEmpty(where)) {
                where = where + MFSqlBase.AND;
            }
            where = where + item.getKey() + "= ? ";
            Object value = MFReflect.getProperty(this, item.getValue());
            argls.add(value);
        }

        String sSQL = MFSqlDelete.getDeleteSql(tableName, where, argls.toArray());

        return adapter.execSql(sSQL);

    }

    public int delete() throws Throwable {

        if (dataAdapter == null) {
            MFDataAdapter da = getAutoDataAdapter();
            int i = delete(da);
            da.free();
            da = null;
            return i;
        } else {
            return delete(dataAdapter);
        }
    }

    public static int execSql(String sql, Object... cmdParams) throws SQLException {
        return execSql(null, sql, cmdParams);
    }

    public static int execSql(MFDataAdapter dataAdapter, String sql, Object... cmdParams) throws SQLException {
        if (dataAdapter == null) {
            MFDataAdapter da = getAutoDataAdapter();
            int i = da.execSql(sql, cmdParams);
            da.free();
            da = null;
            return i;
        } else {
            return dataAdapter.execSql(sql, cmdParams);
        }
    }

    public static <T> int getCount(Class<T> classType, String where, Object... args) {
        return getCount(null, classType, where, args);
    }

    public static <T> int getCount(Class<T> classType) {
        return getCount(null, classType, MFString.Empty, new Object[1]);
    }

    public static <T> int getCount(MFDataAdapter dataAdapter, Class<T> classType) {
        return getCount(dataAdapter, classType, MFString.Empty, new Object[1]);
    }

    public static <T> int getCount(MFDataAdapter dataAdapter, Class<T> classType, String where, Object... args) {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(classType);
        String tableName = cInfo.getTableViewName();

        String sWhere = MFString.Empty;
        if (MFString.notNullOrEmpty(where)) {
            sWhere = MFSqlSelect.WHERE + MFSqlSelect.getStaticSql(where, args);
        }
        String sSQL = String.format(MFSqlSelect.SELECT_COUNT, tableName, sWhere);

        Object iCount = -1;

        try {

            if (dataAdapter == null) {
                MFDataAdapter da = getAutoDataAdapter();
                iCount = da.execScalar(sSQL);
                da.free();
                da = null;
            } else {
                iCount = dataAdapter.execScalar(sSQL);
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }

        BigDecimal i = (BigDecimal) iCount;
        return i.intValue();

    }

    public void setDataAdapter(MFDataAdapter dataAdapter) {
        this.dataAdapter = dataAdapter;
    }

    public MFDataAdapter getDataAdapter() {
        return dataAdapter;
    }

    public static <T> int updateList(Class<T> classType, List<T> objectList) throws Throwable {
        int i = 0;
        for (T item : objectList) {
            i = i + ((MFActiveRecordBase) item).update();
        }
        return i;
    }

    public static <T> int updateList(MFDataAdapter adapter, Class<T> classType, List<T> objectList) throws Throwable {
        int i = 0;
        for (T item : objectList) {
            i = i + ((MFActiveRecordBase) item).update(adapter);
        }
        return i;
    }

    public static <T> int insertList(MFDataAdapter adapter, Class<T> classType, List<T> objectList) throws Throwable {
        int i = 0;
        for (T item : objectList) {
            i = i + ((MFActiveRecordBase) item).insert(adapter);
        }
        return i;
    }

    public static <T> int insertList(Class<T> classType, List<T> objectList) throws Throwable {
        int i = 0;
        for (T item : objectList) {
            i = i + ((MFActiveRecordBase) item).insert();
        }
        return i;
    }

    public static <T> int deleteList(Class<T> classType, List<T> objectList) throws Throwable {
        int i = 0;
        for (T item : objectList) {
            i = i + ((MFActiveRecordBase) item).delete();
        }
        return i;
    }

    public static <T> int deleteList(MFDataAdapter adapter, Class<T> classType, List<T> objectList) throws Throwable {
        int i = 0;
        for (T item : objectList) {
            i = i + ((MFActiveRecordBase) item).delete(adapter);
        }
        return i;
    }
}
